/******************************************************************************
 * @brief    音频频谱
 *
 * Copyright (c) 2013~2020, <morro_luo@163.com>
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Chovere Logs:
 * Date           Author       Notes
 * 2013-05-04     Morro        初版
 * 2016-02-27     Morro        按模块化方式重写
 * 2020-09-20     Morro        优化排版结构以及描述
 ******************************************************************************/
#include "spectrum.h"
#include "audio_sample.h"
#include "cr2fft.h"
#include "timer_tick.h"


/*信号处理 -------------------------------------------------------------------*/
typedef struct {
    unsigned short audio_data[NPT * 3];       //音频数据
    complex_t      input[NPT];
}signal_handler_t;

static signal_handler_t sig;

#if 0
static spectrum_view_t view =  {
    .buoy_hover_time  = 120,               //浮标悬停时间
    .buoy_fall_time   = 30,
    .buoy_color       = LED_COLOR_RED,
    .bar_rise_time    = 10,
    .bar_fall_time    = 20,
    .bar_color        = LED_COLOR_BLUE,
};
#else
static spectrum_view_t view =  {
        .buoy_hover_time  = 6,               //浮标悬停时间
        .buoy_fall_time   = 8,
        .buoy_color       = LED_COLOR_RED,
        .bar_rise_time    = 3,
        .bar_fall_time    = 4,
        .bar_color        = LED_COLOR_BLUE
};
#endif


//频谱分析初始化
void spectrum_init(void)
{
    //3倍采样,后续会每3点做平均
    audio_sample_init(sig.audio_data, NPT * 3);
    audio_sample_start();
}

//开方快速算法
float InvSqrt(float x)
{
    float xhalf = 0.5f * x;
    int i = *(int*)&x;
    i = 0x5f3759df - (i >> 1);        // 计算第一个近似根
    x = *(float*)&i;
    x = x*(1.5f - xhalf * x * x);    // 牛顿迭代法
    return x;
}

//计算功率谱(P = sqrt(r * r + i * i) / NPT)
//#pragma optimize=none
void calc_power(spectrum_view_t *v, complex_t *input)
{
    int    power_table[LED_MATRIX_WIDTH];
    int    max_power = 0, power;
    int i, j;
    long long real, image;
    float     factor = 1.0;           //自动增益控制因子

    //P = sqrt(r * r + i * i) / NPT
    //计算功率谱
    for(i = 0, j = 0; i < LED_MATRIX_WIDTH; i++) {
        j++;
        real  = input[j].real;
        image = input[j].imag;
        power = (int)(1.0 / InvSqrt(real * real + image * image));
        power /= NPT;
        power_table[i] = power;

        if (power > max_power)               //统计功率谱中的最大值,用于增益控制
            max_power = power;
    }

    factor = max_power >= 38 ? max_power / 38.0 : 1.0001;
    for(i = 0; i < LED_MATRIX_WIDTH; i++) {
        power = (int)(power_table[i] / factor);     /*增益控制*/
        v->power[i] = power > LED_MATRIX_HEIGHT ? LED_MATRIX_HEIGHT : power;
    }

}

/*
 * @brief       频谱处理任务
 * @return      none
 */
void spectrum_process(void)
{
    int i;
    if(audio_sample_completed())   {	//采样完成

        for (i = 0; i < NPT; i++) {
            sig.input[i].real = (sig.audio_data[3 * i] +
                                 sig.audio_data[3 * i + 1] +
                                 sig.audio_data[3 * i + 2]) / 3;
            sig.input[i].imag = 0;
        }

        audio_sample_start();          //启动下一次采样

        cr2_fft(sig.input, NPT);

        calc_power(&view, sig.input);  //计算功率谱
        spectrum_view_render();
    }
}


/**
  * @brief  频谱视图渲染
  * @param  none
  * @retval none
  */
void spectrum_view_render(void)
{
    int  i, j;
    spectrum_view_t *v = &view;
    int         power;
    bar_obj_t  *bar;
    buoy_obj_t *buoy;

    for (i = 0; i < LED_MATRIX_WIDTH; i++) {
        bar  = &v->bar[i];
        buoy = &v->buoy[i];
        power = v->power[i];

        //谱柱管理
        if (power != bar->value) {                /*谱能量值与当前柱高度不一致*/
            if (power < bar->value) {             /*下落时间到*/
                if (is_timeout(bar->fall_timer, v->bar_fall_time)) {
                    bar->value--;
                    bar->fall_timer = get_tick();
                }
            } else {
                bar->value = power;
            }
        }

        //浮标管理
        if (bar->value != buoy->value){
            if (bar->value > buoy->value) {
                buoy->value       = bar->value;   /*确保浮标在最上面*/
                buoy->hover_timer = get_tick();   /*开始悬停计时*/
            }
            if (is_timeout(buoy->hover_timer, v->buoy_hover_time)) {  //悬停超时
                if (is_timeout(buoy->fall_timer, v->buoy_fall_time)) {
                    buoy->fall_timer = get_tick();
                    buoy->value--;
                }
            }
        }

        //绘制谱柱
        for (j = 0; j < LED_MATRIX_HEIGHT - bar->value; j++)
        {
            int rgbId = getIdByMatrix(i, j);
            SetRgbBufferByID(rgbId, (Color_t)LED_COLOR_BLACK, 0);
        }


        for (; j < LED_MATRIX_HEIGHT; j++)
        {
            int rgbId = getIdByMatrix(i, j);
            SetRgbBufferByID(rgbId, v->bar_color, 1);
        }

        if (buoy->value == 0)                       /*浮标显示最底下*/
            buoy->value =1;

        //绘制浮标
        int rgbId = getIdByMatrix(i, LED_MATRIX_HEIGHT - buoy->value);
        SetRgbBufferByID(rgbId, v->buoy_color, 1);
        SyncLights();
    }
}
/**
  * @brief      设置频谱显示参数
  * @param[in]  v - 视图参数
  * @retval     none
  */
void spectrum_config_set(spectrum_view_t *v)
{
    view = *v;
}

/**
  * @brief      获取频谱显示参数
  * @param[out] v - 视图参数
  * @retval     none
  */
void spectrum_config_get(spectrum_view_t *v)
{
    *v = view;
}
